import datetime
import hashlib

from BlockChain.分模块实现简单区块链.Transaction import Transaction


class DaDaMessage:  # 交易记录类
    def __init__(self, data):
        self.hash = None  # 自身的哈希
        self.prev_hash = None  # 上一个信息记录的哈希
        self.timestamp = datetime.datetime.now()  # 交易时间
        self.data = data  # 交易信息
        self.payload_hash = self._hash_payload()  # 交易后的哈希

    def _hash_payload(self):  # 对于交易时间与交易数据进行哈希计算
        return hashlib.sha256((str(self.timestamp) + str(self.data)).encode()).hexdigest()

    def _hash_message(self):  # 对于交易进行锁定
        return hashlib.sha256((str(self.prev_hash) + str(self.payload_hash)).encode()).hexdigest()

    def seal(self):  # 密封
        self.hash = self._hash_message()  # 对应数据锁定，对于交易前的链锁定

    def validate(self):  # 验证
        if self.payload_hash != self._hash_payload():  # 判断是否有人修改
            raise InvalidMessage("交易数据与时间被修改" + str(self))
        if self.hash != self._hash_message():  # 判断消息链
            raise InvalidMessage("交易的哈希链接被修改" + str(self))
        return "数据正常" + str(self)

    def __repr__(self):  # 返回对象的基本信息
        mystr = "hash:{}, prev_hash:{}, data:{}".format(self.hash, self.prev_hash, self.data)
        return mystr

    def link(self, Message):
        self.prev_hash = Message.hash  # 链接


class InvalidMessage(Exception):  # 异常类型
    def __init__(self, *args, **kwargs):
        Exception.__init__(self, *args, **kwargs)


if __name__ == '__main__':
    try:
        t1 = Transaction("x", "y1", 1)
        t2 = Transaction("x", "y2", 2)
        t3 = Transaction("x", "y3", 3)
        t4 = Transaction("x", "y4", 4)

        print(t2)

        m1 = DaDaMessage(t1)  # 交易记录
        m2 = DaDaMessage(t2)
        m3 = DaDaMessage(t3)
        m4 = DaDaMessage(t4)
        m1.seal()
        m2.link(m1)  # 链接
        m2.seal()
        m3.link(m2)
        m3.seal()
        m4.link(m3)
        m4.seal()

        print(m2)

        # 修改数据，模拟篡改
        # m2.data = "2的直播"
        # m2.link(m4)
        m2.validate()
    except InvalidMessage as e:
        print(e)
